// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/bluetooth/web_bluetooth_device_id.h"

#include "base/base64.h"
#include "base/strings/string_util.h"
#include "crypto/random.h"

namespace content {

namespace {

    enum { kDeviceIdLength = 16 /* 128 bits */ };

} // namespace

WebBluetoothDeviceId::WebBluetoothDeviceId() { }

WebBluetoothDeviceId::WebBluetoothDeviceId(std::string device_id)
    : device_id_(std::move(device_id))
{
    CHECK(IsValid(device_id_));
}

WebBluetoothDeviceId::~WebBluetoothDeviceId() { }

const std::string& WebBluetoothDeviceId::str() const
{
    CHECK(IsValid(device_id_));
    return device_id_;
}

// static
WebBluetoothDeviceId WebBluetoothDeviceId::Create()
{
    std::string bytes(
        kDeviceIdLength + 1 /* to avoid bytes being reallocated by WriteInto */,
        '\0');

    crypto::RandBytes(base::WriteInto(&bytes /* str */,
                          kDeviceIdLength + 1 /* length_with_null */),
        kDeviceIdLength);

    base::Base64Encode(bytes, &bytes);

    return WebBluetoothDeviceId(std::move(bytes));
}

// static
bool WebBluetoothDeviceId::IsValid(const std::string& device_id)
{
    std::string decoded;
    if (!base::Base64Decode(device_id, &decoded)) {
        return false;
    }

    if (decoded.size() != kDeviceIdLength) {
        return false;
    }

    // When base64-encoding a 128bit string, only the two MSB are used for
    // the 3rd-to-last character. Because of this, the 3rd-to-last character
    // can only be one of this four characters.
    if (!(device_id[device_id.size() - 3] == 'A' || device_id[device_id.size() - 3] == 'Q' || device_id[device_id.size() - 3] == 'g' || device_id[device_id.size() - 3] == 'w')) {
        return false;
    }

    return true;
}

bool WebBluetoothDeviceId::operator==(
    const WebBluetoothDeviceId& device_id) const
{
    return str() == device_id.str();
}

bool WebBluetoothDeviceId::operator!=(
    const WebBluetoothDeviceId& device_id) const
{
    return !(*this == device_id);
}

std::ostream& operator<<(std::ostream& out,
    const WebBluetoothDeviceId& device_id)
{
    return out << device_id.str();
}

} // namespace content
